package com.clei.utils;

import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collection;
import java.util.Random;
import java.util.StringJoiner;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 字符串工具类
 *
 * @author KIyA
 */
public final class StringUtil {

    /**
     * utf8 \\u字符串 pattern
     */
    private static final Pattern UTF_STR_PATTERN = Pattern.compile("(\\\\u[0-9A-Fa-f]{4})+");

    private StringUtil() {
    }

    public static String createOrderNo() {
        String timestamp = DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss");
        Integer max = 999999;
        int randomInt = new Random().nextInt(999999);
        StringBuilder sb = new StringBuilder();
        sb.append(timestamp);
        int length = String.valueOf(max).length() - String.valueOf(randomInt).length();
        for (int i = 0; i < length; i++) {
            sb.append('0');
        }
        sb.append(randomInt);
        return sb.toString();
    }

    public static boolean isNotEmpty(String str) {
        return !isEmpty(str);
    }

    public static boolean isEmpty(String str) {
        return null == str || str.isEmpty();
    }

    /**
     * 是否是空白字符串
     *
     * @param str
     * @return
     */
    public static boolean isBlank(String str) {
        if (isNotEmpty(str)) {
            char[] arr = str.toCharArray();
            for (char c : arr) {
                if (!Character.isWhitespace(c)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 是否不是空白字符串
     *
     * @param str
     * @return
     */
    public static boolean isNotBlank(String str) {
        return !isBlank(str);
    }

    /**
     * 去掉空白
     *
     * @param str
     * @return
     */
    public static String trimBlank(String str) {
        if (isEmpty(str)) {
            return str;
        }

        char[] arr = str.toCharArray();

        StringBuilder sb = new StringBuilder();

        for (char c : arr) {
            if (' ' == c || '\t' == c || '\r' == c || '\n' == c) {
                continue;
            }
            sb.append(c);
        }
        return sb.toString();
    }

    /**
     * 蛇形 -> 驼峰
     *
     * @param str SNAKE_CASE String
     * @return LOWER_CAMEL_CASE String
     */
    public static String snake2Camel(String str) {
        // 默认小驼峰
        return snake2Camel(str, false);
    }

    /**
     * 蛇形 -> 驼峰
     *
     * @param str SNAKE_CASE String
     * @param big true大驼峰 false小驼峰
     * @return CAMEL_CASE String
     */
    public static String snake2Camel(String str, boolean big) {
        if (isBlank(str)) {
            return str;
        }
        char separator = '_';
        char[] arr = str.toCharArray();
        // 大驼峰
        if (big && arr.length > 0) {
            arr[0] = Character.toUpperCase(arr[0]);
        }
        StringBuilder sb = new StringBuilder(arr.length);
        boolean meet = false;
        for (char c : arr) {
            if (separator == c) {
                meet = true;
            } else if (meet) {
                sb.append(Character.toUpperCase(c));
                meet = false;
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 获得一个正确格式的url【后缀】
     *
     * @param url
     * @return
     */
    public static String getUrl(String url) {
        if (isNotEmpty(url)) {
            char slash = '/';
            char[] arr = url.toCharArray();
            StringBuilder sb = new StringBuilder(url.length());
            sb.append(slash);
            for (char c : arr) {
                if (slash == c && slash == sb.charAt(sb.length() - 1)) {
                    continue;
                }
                sb.append(c);
            }
            if (sb.length() > 1 && slash == sb.charAt(sb.length() - 1)) {
                sb.deleteCharAt(sb.length() - 1);
            }
            url = sb.toString();
        }
        return url;
    }

    /**
     * str 是否包含 strArr中的某个字符串
     *
     * @param str
     * @param strArr
     * @return
     */
    public static boolean contains(String str, String[] strArr) {
        return containsIndex(str, strArr) > -1;
    }

    /**
     * str 是否包含 strArr中的某个字符串
     *
     * @param str
     * @param strArr
     * @return 第一个包含字符串的索引
     */
    public static int containsIndex(String str, String[] strArr) {

        if (isEmpty(str) || null == strArr || 0 == strArr.length) {
            throw new RuntimeException("参数为空");
        }

        for (int i = 0; i < strArr.length; i++) {
            if (isNotEmpty(strArr[i])) {

                boolean result = str.contains(strArr[i]);

                if (result) {
                    return i;
                }
            }
        }

        return -1;
    }

    /**
     * 是否是字母
     *
     * @param i char c -> i
     * @return true/false
     */
    public static boolean isLetter(int i) {
        return (64 < i && 91 > i) || (96 < i && 124 > i);
    }

    /**
     * 是数字
     *
     * @param str
     * @return
     */
    public static boolean isDigit(String str) {
        String pattern = "\\d*";
        return Pattern.matches(pattern, str);
    }

    /**
     * 是数字或字符
     *
     * @param str
     * @return
     */
    public static boolean isDigitOrChar(String str) {
        String pattern = "[0-9A-Za-z]*";

        return Pattern.matches(pattern, str);
    }

    /**
     * 反转字符串
     *
     * @param str
     * @return
     */
    public static String reverse(String str) {
        // 空直接返回
        if (isEmpty(str)) {
            return str;
        }
        // 转成数组再翻转
        char[] arr = str.toCharArray();
        ArrayUtil.reverse(arr);
        return new String(arr);
    }

    /**
     * 字符串拼接
     * 通过StringJoiner实现
     *
     * @param list       字符串集合
     * @param emptyValue 字符串集合为空时返回的值
     * @param delimiter  分隔符
     * @param prefix     前缀
     * @param suffix     后缀
     * @return
     */
    public static <T> String join(Collection<T> list, String emptyValue, String delimiter, String prefix, String suffix) {
        if (CollectionUtil.isEmpty(list)) {
            return emptyValue;
        }
        StringJoiner joiner = new StringJoiner(delimiter, prefix, suffix);
        for (T t : list) {
            joiner.add(t.toString());
        }
        return joiner.toString();
    }

    /**
     * 字符串拼接
     * 自己通过StringBuilder实现
     *
     * @param <T>       泛型
     * @param list      字符串集合
     * @param delimiter 分隔符
     * @return String
     */
    public static <T> String strJoin(Collection<T> list, String delimiter) {
        return strJoin(list, "", delimiter, "", "");
    }

    /**
     * 字符串拼接
     * 自己通过StringBuilder实现
     *
     * @param <T>        泛型
     * @param list       字符串集合
     * @param emptyValue 字符串集合为空时返回的值
     * @param delimiter  分隔符
     * @param prefix     前缀
     * @param suffix     后缀
     * @return String
     */
    public static <T> String strJoin(Collection<T> list, String emptyValue, String delimiter, String prefix, String suffix) {
        if (CollectionUtil.isEmpty(list)) {
            return emptyValue;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(prefix);
        for (T t : list) {
            sb.append(t.toString());
            sb.append(delimiter);
        }
        if (isNotEmpty(delimiter)) {
            int length = sb.length();
            sb.delete(length - delimiter.length(), length);
        }
        sb.append(suffix);
        return sb.toString();
    }

    /**
     * 字符串拼接
     * 自己通过StringBuilder实现
     *
     * @param <T>       泛型
     * @param arr       泛型对象集合
     * @param delimiter 分隔符
     * @return String
     */
    public static <T> String strJoin(T[] arr, String delimiter) {
        return strJoin(arr, "", delimiter, "", "");
    }

    /**
     * 字符串拼接
     * 自己通过StringBuilder实现
     *
     * @param <T>        泛型
     * @param arr        泛型对象集合
     * @param emptyValue 字符串集合为空时返回的值
     * @param delimiter  分隔符
     * @param prefix     前缀
     * @param suffix     后缀
     * @return String
     */
    public static <T> String strJoin(T[] arr, String emptyValue, String delimiter, String prefix, String suffix) {
        if (ArrayUtil.isEmpty(arr)) {
            return emptyValue;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(prefix);
        for (T t : arr) {
            sb.append(t.toString());
            sb.append(delimiter);
        }
        if (isNotEmpty(delimiter)) {
            int length = sb.length();
            sb.delete(length - delimiter.length(), length);
        }
        sb.append(suffix);
        return sb.toString();
    }

    /**
     * baseUuid
     *
     * @return baseUuid
     */
    public static String baseUuid() {
        return UUID.randomUUID().toString();
    }

    /**
     * uuid
     *
     * @return uuid
     */
    public static String uuid() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    /**
     * 返回一个由str结束
     * 使用字符补全到长度为length的字符串
     *
     * @param str    源字符串
     * @param c      补全用的字符
     * @param length 期望的字符串长度
     * @return String
     */
    public static String complete(String str, char c, int length) {
        return complete(str, true, c, length, true);
    }

    /**
     * 返回一个由str结束
     * 使用字符补全到长度为length的字符串
     *
     * @param str       源字符串
     * @param useLength true使用字符串长度 false使用字符串宽度
     * @param c         补全用的字符
     * @param length    期望的字符串长度
     * @return String
     */
    public static String complete(String str, boolean useLength, char c, int length) {
        return complete(str, useLength, c, length, true);
    }

    /**
     * 返回一个由str结束
     * 使用字符补全到长度为length的字符串
     *
     * @param str    源字符串
     * @param c      补全用的字符
     * @param length 期望的字符串长度
     * @param before 填充字符放的位置 true前 false 后
     * @return String
     */
    public static String complete(String str, char c, int length, boolean before) {
        return complete(str, true, c, length, before);
    }

    /**
     * 返回一个由str结束
     * 使用字符补全到长度为length的字符串
     *
     * @param str       源字符串
     * @param useLength true使用字符串长度 false使用字符串宽度
     * @param c         补全用的字符
     * @param length    期望的字符串长度
     * @param before    填充字符放的位置 true前 false 后
     * @return String
     */
    public static String complete(String str, boolean useLength, char c, int length, boolean before) {
        if (null == str) {
            str = "";
        }
        int strLength = useLength ? str.length() : width(str);
        int charLength = useLength ? 1 : width(c);
        if (strLength >= length) {
            return str;
        }

        StringBuilder sb = new StringBuilder(length);
        if (!before) {
            sb.append(str);
        }
        while (strLength < length) {
            sb.append(c);
            strLength += charLength;
        }
        if (before) {
            sb.append(str);
        }
        return sb.toString();
    }

    /**
     * 去掉字符串尾部的字符
     *
     * @param str String
     * @param c   char
     * @return String
     */
    public static String trimTailChar(String str, char c) {
        int length = str.length();
        int end = length;
        char[] arr = str.toCharArray();
        for (int i = length - 1; i > -1; i--) {
            if (c == arr[i]) {
                end--;
            } else {
                break;
            }
        }
        if (end == length) {
            return str;
        }
        return str.substring(0, end);
    }

    /**
     * utf8String -> String
     * \u751F\u4EA7\u73AF\u5883 -> 生产环境
     *
     * @param str 带\\u的字符串
     * @return 中文字符串
     */
    public static String utf8Str2Str(String str) {
        if (isEmpty(str)) {
            return str;
        }
        Matcher matcher = UTF_STR_PATTERN.matcher(str);
        StringBuilder sb = new StringBuilder();
        int start;
        int end = 0;

        while (matcher.find()) {
            start = matcher.start();
            if (start > end) {
                sb.append(str, end, start);
            }
            end = matcher.end();
            String txt = matcher.group();
            String[] split = txt.split("\\\\u");
            String res = Arrays.stream(split)
                    .filter(s -> !s.isEmpty())
                    .map(s -> {
                        // 十六进制
                        char c = (char) Integer.parseInt(s, 16);
                        return String.valueOf(c);
                    }).collect(Collectors.joining());
            sb.append(res);
        }
        int length = str.length();
        if (end < length) {
            sb.append(str, end, length);
        }
        return sb.toString();
    }

    /**
     * str在source中第一n次出现的位置
     *
     * @param source source
     * @param str    str
     * @param n      第几次出现 0最后一次
     * @return index
     */
    public static int indexOf(String source, String str, int n) {
        if (0 == n) {
            return source.lastIndexOf(str);
        }
        int index = -1;
        int length = str.length();
        int start = 0;
        while (n > 0) {
            index = source.indexOf(str, start);
            n--;
            if (-1 == index) {
                break;
            } else {
                start = index + length;
            }
        }
        return index;
    }

    /**
     * 获取字符串展示宽度
     *
     * @param str str
     * @return width
     */
    public static int width(String str) {
        if (null == str) {
            return 0;
        }
        char[] arr = str.toCharArray();
        int width = 0;
        for (char c : arr) {
            width += width(c);
        }
        return width;
    }

    /**
     * 获取字符展示宽度
     *
     * @param c c
     * @return width
     */
    public static int width(char c) {
        return CharWidth.width(c);
    }

    /**
     * 判断是否是英文字符
     *
     * @param c c
     * @return true/false
     */
    public static boolean isEn(char c) {
        return c < 128;
    }

    /**
     * mapThenPrint
     *
     * @param str    str
     * @param mapper mapper
     * @param <R>    泛型
     */
    public static <R> void mapThenPrint(String str, Function<String, R> mapper) {
        mapThenPrint(str, mapper, "\n");
    }

    /**
     * mapThenPrint
     *
     * @param str       str
     * @param mapper    mapper
     * @param separator separator
     * @param <R>       泛型
     */
    public static <R> void mapThenPrint(String str, Function<String, R> mapper, String separator) {
        mapThenPrint(str, null, mapper, separator);
    }

    /**
     * mapThenPrint
     *
     * @param str       str
     * @param filter    filter
     * @param mapper    mapper
     * @param separator separator
     * @param <R>       泛型
     */
    public static <R> void mapThenPrint(String str, Predicate<String> filter, Function<String, R> mapper, String separator) {
        Stream<String> stream = Arrays.stream(str.split(separator));
        if (null != filter) {
            stream = stream.filter(filter);
        }
        stream.map(mapper)
                .forEach(PrintUtil::println);
    }

    /**
     * 字符宽度
     */
    private static class CharWidth {
        /**
         * 英文FontMetrics 用于获取字符串宽度
         */
        private final static FontMetrics FONT_METRICS_EN = fontMetrics("JetBrains Mono");

        /**
         * 英文FontMetrics 用于获取字符串宽度
         */
        private final static FontMetrics FONT_METRICS_CN = fontMetrics("Microsoft YaHei");

        /**
         * 获取字符展示宽度
         *
         * @param c c
         * @return width
         */
        private static int width(char c) {
            return isEn(c) ? FONT_METRICS_EN.charWidth(c) : FONT_METRICS_CN.charWidth(c);
        }

        /**
         * 获取 FontMetrics
         *
         * @param fontName 字体名称
         * @return FontMetrics
         */
        private static FontMetrics fontMetrics(String fontName) {
            BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
            Graphics graphics = image.getGraphics();
            // size 15获取 Settings -> Editor -> Color Scheme -> Console Font size
            Font font = new Font(fontName, Font.PLAIN, 15);
            // jetBrainsMono获取历程
            // 1. Settings -> Editor -> Color Scheme -> Console Font 获取控制台打印用的字体
            // 2. IDEA安装目录下找到字体 D:\IDEA\jbr\lib\fonts
            // 3. 安装字体 使用GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()校验是否可获取到
            // 4. 上述方法未获取到，重新安装，右键点击 选择为所有用户安装
            return graphics.getFontMetrics(font);
        }
    }
}
